package com.langrensha.langrensha.netty;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.langrensha.langrensha.store.RoomStore;
import com.langrensha.langrensha.store.UserStore;
import com.langrensha.langrensha.entity.RoomStatus;
import com.langrensha.langrensha.entity.UserInfo;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

import static com.langrensha.langrensha.entity.UserStatusEnum.*;

@Slf4j
public class MainHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private static final ObjectMapper objectMapper = new ObjectMapper();
    public static Random random = new Random();

    @Override
    protected void channelRead0(
            ChannelHandlerContext channelHandlerContext, TextWebSocketFrame text)
            throws JsonProcessingException {
        Channel incoming = channelHandlerContext.channel();
        String message = text.text();

        String[] msgArray = message.split("-");
        switch (msgArray[0]) {
            case "hello":
                incoming.writeAndFlush(new TextWebSocketFrame("hello client"));
                break;
            case "man":
                manHandler(incoming, msgArray);
                break;
            case "admin":
                adminHandler(msgArray);
                break;
            case "wolf":
                wolfHandler(msgArray);
                break;
            case "witch":
                witchHandler(incoming, msgArray);
                break;
            case "prophet":
                prophetHandler(incoming, msgArray);
            default:
                break;
        }
    }

    public void manHandler(Channel incoming, String[] msgArray) throws JsonProcessingException {
        String action = msgArray[1];
        String hostId = msgArray[4];
        String userName = msgArray[2];

        UserInfo u = UserStore.getByName(hostId, userName);

        switch (action) {
            case "link":
                if (u == null) {
                    u = new UserInfo(hostId, userName, incoming, LOOKING);
                    UserStore.addImageInfo(u);
                    UserStore.save(u);
                } else if (incoming == u.getChannel()) {
                    break;
                } else {
                    userReconnect(hostId, u, incoming);
                }
                // 广播
                UserInfo finalUserInfo = u;
                RoomStore.getRoomChannel(hostId)
                        .forEach(
                                c ->
                                        c.writeAndFlush(
                                                new TextWebSocketFrame(
                                                        "link-{}-"
                                                                + userName
                                                                + "-"
                                                                + finalUserInfo.getImg())));
                // 单播当前房间的座位信息.
                Map<String, String> seatInfo = RoomStore.getSeatInfo(hostId);
                incoming.writeAndFlush(
                        new TextWebSocketFrame(
                                "link-"
                                        + objectMapper.writeValueAsString(seatInfo)
                                        + "-"
                                        + u.getName()
                                        + "-"
                                        + u.getImg()));
                RoomStore.addChannel(hostId, incoming);

                break;
            case "seat":
                {
                    String seatId = msgArray[3];
                    if (seatId.equals(u.getSeatId())) break;
                    ChannelGroup seatChannels = RoomStore.getSeatChannel(hostId);
                    // 换座位,查看当前座位是否有人入座
                    if (UserStore.getBySeatId(hostId, seatId) != null) {
                        // 单播当前位置已经有人入座了
                        incoming.writeAndFlush(new TextWebSocketFrame("seat-fail"));
                        break;
                    }
                    if (u.getSeatId() != null) {
                        // 清空原来的座位信息
                        UserStore.removeSeatInfo(hostId, u.getSeatId());
                        RoomStore.removeSeatInfo(hostId, u.getSeatId());
                    } else {
                        seatChannels.add(incoming);
                    }
                    u.setSeatId(seatId);
                    u.setStatus(SEAT);
                    RoomStore.addSeatInfo(hostId, seatId, userName);
                    RoomStore.getRoomChannel(hostId)
                            .forEach(
                                    channel ->
                                            channel.writeAndFlush(
                                                    new TextWebSocketFrame(
                                                            "seat-" + userName + "-" + seatId)));
                    RoomStore.addSeatChannel(hostId, u.getChannel());
                    RoomStore.addSeatUser(hostId, u);
                    UserStore.addSeatUserINFO(hostId, seatId, u);

                    break;
                }
            case "stand":
                {
                    String seatId = msgArray[2];
                    u = UserStore.getBySeatId(hostId, seatId);
                    RoomStore.getRoomChannel(hostId)
                            .forEach(
                                    c ->
                                            c.writeAndFlush(
                                                    new TextWebSocketFrame("stand-" + seatId)));
                    RoomStore.removeSeatChannel(hostId, incoming);
                    RoomStore.removeSeatUser(hostId, u);
                    RoomStore.removeSeatInfo(hostId, seatId);
                    UserStore.removeSeatInfo(hostId, u.getSeatId());
                    u.setStatus(LOOKING);
                    u.setSeatId(null);
                    break;
                }
        }
    }

    private void userReconnect(String hostId, UserInfo u, Channel incoming) {
        u.setChannel(incoming);
        if (RoomStore.getRoom(hostId).getStatus() == RoomStatus.BEGIN) {
            u.setStatus(PLAYING);
        } else if (u.getSeatId() != null) {
            u.setStatus(SEAT);
            RoomStore.addSeatChannel(hostId, incoming);
            RoomStore.addSeatUser(hostId, u);
        } else {
            u.setStatus(LOOKING);
        }
        RoomStore.addChannel(hostId, incoming);
        UserStore.save(u);
    }

    public void adminHandler(String[] msgArray) {

        String hostId = msgArray[4];
        String action = msgArray[1];
        ChannelGroup seatChannels = RoomStore.getSeatChannel(hostId);
        List<UserInfo> seatedUserList = RoomStore.getSeatUser(hostId);
        switch (action) {
            case "card":
                List<Integer> roles = RoomStore.getRole(hostId);
                List<Integer> array = new ArrayList<>();
                Random random = new Random();
                Set<Integer> set = new HashSet<>();
                while (set.size() < roles.size()) {
                    int randomNumber = random.nextInt(roles.size());
                    if (set.add(randomNumber)) {
                        array.add(roles.get(randomNumber));
                    }
                }

                int i = 0;
                for (UserInfo userInfo : seatedUserList) {
                    userInfo.setRole(array.get(i).toString());
                    if (userInfo.getChannel() != null && userInfo.getChannel().isActive()) {
                        userInfo.getChannel()
                                .writeAndFlush(new TextWebSocketFrame("card-" + array.get(i)));
                    }
                    i++;
                }

                break;
            case "begin":
                ChannelGroup roomGroup = RoomStore.getRoomChannel(hostId);
                log.info("开始游戏!");
                roomGroup.forEach(
                        channel -> channel.writeAndFlush(new TextWebSocketFrame("begin--")));
                RoomStore.getSeatChannel(hostId)
                        .forEach(c -> UserStore.getByChannel(c).setStatus(PLAYING));
                RoomStore.begin(hostId);
                break;
            case "night":
                List<String> nightInfo = RoomStore.getNightInfo(hostId);
                String witchPoison = RoomStore.getPoison(hostId);
                StringBuilder sb = new StringBuilder("night-");
                if (nightInfo != null && nightInfo.size() != 0) {
                    sb.append(nightInfo.get(nightInfo.size() - 1));
                }
                sb.append("-");
                sb.append(witchPoison);
                String text = sb.toString();
                seatChannels.forEach(c -> c.writeAndFlush(new TextWebSocketFrame(text)));
                break;
        }
    }

    public void wolfHandler(String[] msgArray) {

        String action = msgArray[1];
        String from = msgArray[2];
        String to = msgArray[3];
        String hostId = msgArray[4];
        if (action.equals("kill")) {
            String sb = "kill-" + from + "-" + to;
            RoomStore.getSeatChannel(hostId)
                    .forEach(c -> c.writeAndFlush(new TextWebSocketFrame(sb)));
            RoomStore.addKillInfo(hostId, to);

        } else if (action.equals("killEnd")) {
            // 测试平安夜场景
            String died;
            StringBuilder sb = new StringBuilder("killEnd-");
            List<String> array = RoomStore.getKillInfo(hostId);
            if (array != null && array.size() != 0) {
                int rom = new Random().nextInt(array.size());
                died = array.get(rom);
                sb.append(died);
                RoomStore.addNightInfo(hostId, died);
            }
            RoomStore.getSeatChannel(hostId)
                    .forEach(c -> c.writeAndFlush(new TextWebSocketFrame(sb.toString())));
        }
    }

    public void witchHandler(Channel incoming, String[] msgArray) {
        String action = msgArray[1];
        String hostId = msgArray[4];
        List<String> killInfo = RoomStore.getNightInfo(hostId);
        switch (action) {
            case "die":
                {
                    if (killInfo.size() == 0) {
                        incoming.writeAndFlush(new TextWebSocketFrame("die-"));
                    } else {
                        String killed = killInfo.get(killInfo.size() - 1);
                        incoming.writeAndFlush(new TextWebSocketFrame("die-" + killed));
                    }

                    break;
                }
            case "poison":
                {
                    String poisonId = msgArray[2];
                    String killed;
                    if (killInfo.size() != 0) {
                        killed = killInfo.get(killInfo.size() - 1);
                        if (random.nextInt(2) == 1) {
                            killed = poisonId + "," + killed;
                        } else {
                            killed = killed + "," + poisonId;
                        }
                        killInfo.set(killInfo.size() - 1, killed);
                    } else {
                        killInfo.add(poisonId);
                    }

                    RoomStore.setPoison(hostId, poisonId);
                    incoming.writeAndFlush(new TextWebSocketFrame("ok"));
                    break;
                }
            case "save":
                if (killInfo.size() > 0) {
                    killInfo.remove(killInfo.size() - 1);
                }
                incoming.writeAndFlush(new TextWebSocketFrame("ok"));
                break;
        }
    }

    public void prophetHandler(Channel incoming, String[] msgArray) {
        String seatId = msgArray[2];
        String hostId = msgArray[4];

        UserInfo userInfo = UserStore.getBySeatId(hostId, seatId);
        incoming.writeAndFlush(
                new TextWebSocketFrame("check-" + userInfo.getSeatId() + "-" + userInfo.getRole()));
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        log.info("有新客户端连接");
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        UserInfo u = UserStore.getByChannel(ctx.channel());
        if (u != null) {
            UserStore.handChannelRemove(u);
            // 广播玩家离开房间
            RoomStore.getRoomChannel(u.getHostId())
                    .forEach(
                            c -> {
                                c.writeAndFlush(new TextWebSocketFrame("玩家" + u.getName() + "离线!"));
                                log.info("玩家" + u.getName() + "离线!");
                            });
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        UserInfo u = UserStore.getByChannel(ctx.channel());
        if (u != null) {
            log.info("用户:{}不活跃.用户当前信息:{}", u.getName(), u);
        }
    }

    /**
     * 发生异常。
     *
     * @param ctx ctx
     * @param cause cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        Channel incoming = ctx.channel();
        incoming.writeAndFlush(new TextWebSocketFrame("Exception:" + cause.getMessage()));
        log.error("Exception:{},e:{}", cause.getMessage(), cause);
    }
}
